using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Obsidian.GameObjects;
using Obsidian.Components;
using XConsoleApp;

namespace Obsidian
{
    /// <summary>
    /// Holds all entities in Obsidian.
    /// </summary>
    public class Scene
    {
        public List<GameObject> RootGameObjects;

        public DrawingContext context;

        public ContentManager Content;

        public GraphicsDevice GraphicsDevice;

        private DemoKeyboardService keyboardService = new DemoKeyboardService();
        private Game game;

        public Scene(Game game)
        {
            this.game = game;
        }

        public void Init()
        {
            // attempt to get the graphics service
            IGraphicsDeviceService graphics = null;
            try { graphics = game.Services.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService; }
            catch { graphics = null; }
            finally { if (graphics == null) throw new InvalidOperationException("Could not find IGraphicsDeviceService in services."); }

            GraphicsDevice = graphics.GraphicsDevice;

            // Add the keyboard service to the services.
            game.Services.AddService(typeof(XConsoleApp.XConsole.IKeyboardInputService), keyboardService);

            // create our content manager
            Content = new ContentManager(game.Services);

            var console = new XConsole(game.Services, "Content/ConsoleFont");
            // Initialize the XConsole. We must use Content/ in the font because it is relative to the executable.
            game.Components.Add(console);

            // Register a string parser with the console so we can set field/property values and invoke methods with parameters
            //XConsole.Instance.StringParser = new DemoStringParser();

            // Open the console so it's visible from the start
            XConsole.Instance.Open(false);

            context = new DrawingContext(GraphicsDevice, Content.Load<SpriteFont>("Content/debugFont"));
            RootGameObjects = new List<GameObject>();
        }

        public void Initialize()
        {
            foreach (var gameObject in RootGameObjects)
            {
                gameObject.Initialize();
            }
        }

        public void Update()
        {
            // Update the keyboard service
            keyboardService.Update();

            // TODO - this is where all the message handling and script stuff happens
            // here we update each gameObject, which in turn updates all it's components
            foreach (var gameObject in RootGameObjects)
            {
                gameObject.Update();
            }
        }

        public void RenderScene()
        {
            // TODO - we need to iterate through each camera in the scene based on depth
            //              be sure to use camera.Render()
            //              throw an error if no camera is available

            context.Open();

            foreach (var gameObject in RootGameObjects)
            {
                if (gameObject.Renderer != null)
                {
                    Matrix identity = Matrix.Identity;
                    gameObject.Renderer.Draw(context, ref identity);
                }
            }

            context.Close();
        }
    }
}
